#include "global.h"

LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
D3DPRESENT_PARAMETERS d3dpp;
LPD3DXSPRITE d3dspt;

// Create Direct3D and the Direct3D Device
void InitDirect3D(GAMEWINDOW* gw)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.Windowed = gw->Windowed;
    d3dpp.BackBufferWidth = gw->Width;
    d3dpp.BackBufferHeight = gw->Height;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                                D3DDEVTYPE_HAL,
                                gw->hWnd,
                                D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                &d3dpp,
                                &d3ddev);

    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);

    return;
}

// Close the Device and Direct3D
void CloseDirect3D()
{
    d3ddev->Release();
    d3d->Release();

    return;
}

// Start rendering
void StartRender()
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->BeginScene();

    return;
}

// Stop rendering
void EndRender()
{
    d3ddev->EndScene();
    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}

// Load a mesh
void LoadModel(MODEL* Model, LPCTSTR File)
{
    LPD3DXBUFFER bufMaterial;

    D3DXLoadMeshFromX(File,
                      D3DXMESH_SYSTEMMEM,
                      d3ddev,
                      NULL,
                      &bufMaterial,
                      NULL,
                      &Model->numMaterials,
                      &Model->Mesh);

    D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufMaterial->GetBufferPointer();

    Model->Material = new D3DMATERIAL9[Model->numMaterials];
    Model->Texture = new LPDIRECT3DTEXTURE9[Model->numMaterials];

    for(DWORD index = 0; index < Model->numMaterials; index++)
    {
        Model->Material[index] = tempMaterials[index].MatD3D;
        Model->Material[index].Ambient = Model->Material[index].Diffuse;

        USES_CONVERSION;
        if(FAILED(D3DXCreateTextureFromFile(d3ddev,
                                            CA2W(tempMaterials[index].pTextureFilename),
                                            &Model->Texture[index])))
            Model->Texture[index] = NULL;
    }

    return;
}

// Set the fixed properties of the 3D camera
void SetCamera()
{
    D3DXMATRIX matProjection;
    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),
                               (FLOAT)d3dpp.BackBufferWidth/(FLOAT)d3dpp.BackBufferHeight,
                               1.0f,
                               100.0f);

    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);

    return;
}

// Set the adjustable properties of the 3D camera
void AdjustCamera(float x, float y, float z)
{
    D3DXMATRIX matView;

    D3DXMatrixLookAtLH(&matView,
                       &D3DXVECTOR3 (x, y, z),
                       &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),
                       &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));

    d3ddev->SetTransform(D3DTS_VIEW, &matView);

    return;
}

// Draw the Specified Mesh
void DrawModel(MODEL* Model, float x, float y, float z)
{
    D3DXMATRIX matTranslate;
    D3DXMatrixTranslation(&matTranslate, x, y, z);
    d3ddev->SetTransform(D3DTS_WORLD, &matTranslate);

    for(DWORD index = 0; index < Model->numMaterials; index++)
    {
        d3ddev->SetMaterial(&Model->Material[index]);
        if(Model->Texture[index] != NULL)
            d3ddev->SetTexture(0, Model->Texture[index]);

        Model->Mesh->DrawSubset(index);
    }

    return;
}